﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using AppFx.ComponentModel.Descriptive;
using AppFx.Data.Sort;
using AppFx.Interpretation;
using AppFx.Reflection;
using AppFx.Interpretation.Evaluation;

namespace AppFx.Collections
{
	public class ObjectView<T> : IIndexList, ITypedList, IEnumerable<ItemView<T>>, ICollection<ItemView<T>>, IList<ItemView<T>>, IBindingList, IBindingListView
	{
		#region Properties

		/// <summary>
		/// Gibt oder setzt den ITypeDescriptionProvider, der zur Auffindung von Indizes verwendet wird.
		/// </summary>
		public ITypeDescriptionProvider DescriptionProvider { get; set; }

		/// <summary>
		/// Gibt oder setzt den zur Auswertung von Filtern verwendeten IEvaluator.
		/// </summary>
		public IFilterEvaluator FilterEvaluator { get; set; }

		/// <summary>
		/// Eine Liste aller von der Klasse geführten Indizes/Spalten
		/// </summary>
		public IList<PropertyDescriptor> Indices { get; protected set; }

		/// <summary>
		/// Gibt oder setzt den Namen der Liste.
		/// </summary>
		public string ListName { get; set; }

		#region Lists

		/// <summary>
		/// Die zugrunde liegende Basis-Liste
		/// </summary>
		public IList<T> SourceList { get; protected set; }

		/// <summary>
		/// Die zugrunde liegende Basis-Liste, deren Inhaltsobj
		/// </summary>
		public List<ItemView<T>> MappedList { get; protected set; }

		/// <summary>
		/// Die bereits gefilterte Liste, welche zur Sortierung genutzt wird.
		/// </summary>
		public List<ItemView<T>> FilteredList { get; protected set; }

		/// <summary>
		/// Die sortierte Liste, welche letztendlich 'dargestellt' wird.
		/// </summary>
		public List<ItemView<T>> SortedList { get; protected set; }

		#endregion

		#endregion

		#region Constructors

		/// <summary>
		/// Erstellt eine neue ObjectView.
		/// </summary>
		/// <param name="source">Die Nutzung dieses Parameters bedingt die Konstruktion einer neuen Liste.</param>
		public ObjectView(IEnumerable<T> source) 
			: this(source.ToList()) { }

		/// <summary>
		/// Erstellt eine neue ObjectView.
		/// </summary>
		/// <param name="source">Die Nutzung dieses Parameters bedingt die Konstruktion einer neuen Liste.</param>
		/// <param name="descriptionProvider">Den ITypeDescriptionProvider, der zur Auffindung von Indizes verwendet wird.</param>
		public ObjectView(IEnumerable<T> source, ITypeDescriptionProvider descriptionProvider)
			: this(source.ToList(), descriptionProvider) { }

		/// <summary>
		/// Erstellt eine neue ObjectView.
		/// </summary>
		/// <param name="source">Die Quellliste, welche von der ObjectView gespeichert wird.</param>
		public ObjectView(IList<T> source)
		{
			if (source == null)
				throw new ArgumentNullException("source");

			//Basis-Liste merken.
			SourceList = source;

			//Jedes in der Basis-Liste enthaltene Objekt in eine ItemView projizieren.
			MappedList = new List<ItemView<T>>(SourceList.Select(o => new ItemView<T>(o)));

			FilteredList = new List<ItemView<T>>(MappedList);
			SortedList = new List<ItemView<T>>(MappedList);

			FilterEvaluator = new FleeFilterEvaluator();
			DescriptionProvider = new DeepCompiledTypeDescriptionProvider();
			Indices = DescriptionProvider.GetDescriptors(typeof(T));
		}

		/// <summary>
		/// Erstellt eine neue ObjectView.
		/// </summary>
		/// <param name="source">Die Quellliste, welche von der ObjectView gespeichert wird.</param>
		/// <param name="descriptionProvider">Den ITypeDescriptionProvider, der zur Auffindung von Indizes verwendet wird.</param>
		public ObjectView(IList<T> source, ITypeDescriptionProvider descriptionProvider)
			: this(source)
		{
			DescriptionProvider = descriptionProvider;
			Indices = DescriptionProvider.GetDescriptors(typeof(T));
		}

		#endregion

		#region Interfaces

		#region ITypedList

		/// <summary>
		/// Gibt die <see cref="T:System.ComponentModel.PropertyDescriptorCollection"/> zurück, die die Eigenschaften für jedes Element darstellt, das bei der Datenbindung verwendet wird.
		/// </summary>
		/// <param name="listAccessors">Ein Array von <see cref="T:System.ComponentModel.PropertyDescriptor"/>-Objekten, die in der Auflistung als bindbar gesucht werden sollen.Dieser Wert kann null sein.</param>
		/// <returns>
		/// Die <see cref="T:System.ComponentModel.PropertyDescriptorCollection"/>, die die Eigenschaften für jedes Element darstellt, das bei der Datenbindung verwendet wird.
		/// </returns>
		public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
		{
			if (DescriptionProvider == null)
				throw new NullReferenceException("The DescriptionProvider for TypedBindingList must not be NULL.");
			if (Indices == null)
				Indices = DescriptionProvider.GetDescriptors(typeof(T));
			return new PropertyDescriptorCollection(Indices.ToArray());
		}

		/// <summary>
		/// Gibt den Namen der Liste zurück.
		/// </summary>
		/// <param name="listAccessors">Ein Array von <see cref="T:System.ComponentModel.PropertyDescriptor"/>-Objekten, für die der Listenname zurückgegeben wird.Dieser Wert kann null sein.</param>
		/// <returns>
		/// Der Name der Liste.
		/// </returns>
		public string GetListName(PropertyDescriptor[] listAccessors)
		{
			if (ListName == null)
				return ToString();
			else
				return ListName;
		}

		#endregion

		#region IIndexList

		/// <summary>
		/// Gibt alle in der Klasse geführten Indizes zurück.
		/// </summary>
		/// <returns>
		/// Gibt eine Liste von PropertyDesciptors zurück.
		/// </returns>
		public IList<PropertyDescriptor> GetIndices()
		{
			return Indices;
		}
	
		#endregion

		#region IEnumerable

		/// <summary>
		/// Gibt einen Enumerator zurück, der die Auflistung durchläuft.
		/// </summary>
		/// <returns>
		/// Ein <see cref="T:System.Collections.Generic.IEnumerator`1"/>, der zum Durchlaufen der Auflistung verwendet werden kann.
		/// </returns>
		public IEnumerator<ItemView<T>> GetEnumerator()
		{
			return SortedList.GetEnumerator();
		}

		/// <summary>
		/// Gibt einen Enumerator zurück, der eine Auflistung durchläuft.
		/// </summary>
		/// <returns>
		/// Ein <see cref="T:System.Collections.IEnumerator"/>-Objekt, das zum Durchlaufen der Auflistung verwendet werden kann.
		/// </returns>
		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return SortedList.GetEnumerator();
		}

		#endregion

		#region ICollection

		/// <summary>
		/// Fügt der <see cref="T:System.Collections.Generic.ICollection`1"/> ein Element hinzu.
		/// </summary>
		/// <param name="item">Das Objekt, das <see cref="T:System.Collections.Generic.ICollection`1"/> hinzugefügt werden soll.</param>
		/// <exception cref="T:System.NotSupportedException"><see cref="T:System.Collections.Generic.ICollection`1"/> ist schreibgeschützt.</exception>
		public void Add(ItemView<T> item)
		{
			SortedList.Add(item);
		}

		/// <summary>
		/// Entfernt alle Elemente aus <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </summary>
		/// <exception cref="T:System.NotSupportedException"><see cref="T:System.Collections.Generic.ICollection`1"/> ist schreibgeschützt. </exception>
		public void Clear()
		{
			SortedList.Clear();
		}

		/// <summary>
		/// Bestimmt, ob <see cref="T:System.Collections.Generic.ICollection`1"/> einen bestimmten Wert enthält.
		/// </summary>
		/// <param name="item">Das im <see cref="T:System.Collections.Generic.ICollection`1"/> zu suchende Objekt.</param>
		/// <returns>
		/// true, wenn sich <paramref name="item"/> in <see cref="T:System.Collections.Generic.ICollection`1"/> befindet, andernfalls false.
		/// </returns>
		public bool Contains(ItemView<T> item)
		{
			return SortedList.Contains(item);
		}

		/// <summary>
		/// Copies to.
		/// </summary>
		/// <param name="array">The array.</param>
		/// <param name="arrayIndex">Index of the array.</param>
		public void CopyTo(ItemView<T>[] array, int arrayIndex)
		{
			SortedList.CopyTo(array, arrayIndex);
		}

		/// <summary>
		/// Ruft die Anzahl der Elemente ab, die in <see cref="T:System.Collections.Generic.ICollection`1"/> enthalten sind.
		/// </summary>
		/// <returns>Die Anzahl der Elemente, die in <see cref="T:System.Collections.Generic.ICollection`1"/> enthalten sind.</returns>
		public int Count
		{
			get { return SortedList.Count; }
		}

		/// <summary>
		/// Ruft einen Wert ab, der angibt, ob <see cref="T:System.Collections.Generic.ICollection`1"/> schreibgeschützt ist.
		/// </summary>
		/// <returns>true, wenn <see cref="T:System.Collections.Generic.ICollection`1"/> schreibgeschützt ist, andernfalls false.</returns>
		public bool IsReadOnly
		{
			get { return false; }
		}

		/// <summary>
		/// Entfernt das erste Vorkommen eines bestimmten Objekts aus <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </summary>
		/// <param name="item">Das aus dem <see cref="T:System.Collections.Generic.ICollection`1"/> zu entfernende Objekt.</param>
		/// <returns>
		/// true, wenn <paramref name="item"/> erfolgreich aus <see cref="T:System.Collections.Generic.ICollection`1"/> gelöscht wurde, andernfalls false. Diese Methode gibt auch dann false zurück, wenn <paramref name="item"/> nicht in der ursprünglichen <see cref="T:System.Collections.Generic.ICollection`1"/> gefunden wurde.
		/// </returns>
		/// <exception cref="T:System.NotSupportedException"><see cref="T:System.Collections.Generic.ICollection`1"/> ist schreibgeschützt.</exception>
		public bool Remove(ItemView<T> item)
		{
			return SortedList.Remove(item);
		}

		#endregion

		#region IList

		/// <summary>
		/// Bestimmt den Index eines bestimmten Elements in der <see cref="T:System.Collections.Generic.IList`1"/>.
		/// </summary>
		/// <param name="item">Das im <see cref="T:System.Collections.Generic.IList`1"/> zu suchende Objekt.</param>
		/// <returns>
		/// Der Index von <paramref name="item"/>, wenn das Element in der Liste gefunden wird, andernfalls -1.
		/// </returns>
		public int IndexOf(ItemView<T> item)
		{
			return SortedList.IndexOf(item);
		}

		/// <summary>
		/// Fügt am angegebenen Index ein Element in die <see cref="T:System.Collections.Generic.IList`1"/> ein.
		/// </summary>
		/// <param name="index">Der nullbasierte Index, an dem <paramref name="item"/> eingefügt werden soll.</param>
		/// <param name="item">Das in die <see cref="T:System.Collections.Generic.IList`1"/> einzufügende Objekt.</param>
		/// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="index"/> ist kein gültiger Index in der <see cref="T:System.Collections.Generic.IList`1"/>.</exception>
		///   
		/// <exception cref="T:System.NotSupportedException">Die <see cref="T:System.Collections.Generic.IList`1"/> ist schreibgeschützt.</exception>
		public void Insert(int index, ItemView<T> item)
		{
			SortedList.Insert(index, item);
		}

		/// <summary>
		/// Entfernt das <see cref="T:System.Collections.Generic.IList`1"/>-Element am angegebenen Index.
		/// </summary>
		/// <param name="index">Der nullbasierte Index des zu entfernenden Elements.</param>
		/// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="index"/> ist kein gültiger Index in der <see cref="T:System.Collections.Generic.IList`1"/>.</exception>
		///   
		/// <exception cref="T:System.NotSupportedException">Die <see cref="T:System.Collections.Generic.IList`1"/> ist schreibgeschützt.</exception>
		public void RemoveAt(int index)
		{
			SortedList.RemoveAt(index);
		}

		/// <summary>
		/// Ruft das Element am angegebenen Index ab oder legt dieses fest.
		/// </summary>
		/// <returns>Das Element am angegebenen Index.</returns>
		///   
		/// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="index"/> ist kein gültiger Index in der <see cref="T:System.Collections.Generic.IList`1"/>.</exception>
		///   
		/// <exception cref="T:System.NotSupportedException">Die Eigenschaft wird festgelegt, und die <see cref="T:System.Collections.Generic.IList`1"/> ist schreibgeschützt.</exception>
		public ItemView<T> this[int index]
		{
			get
			{
				return SortedList[index];
			}
			set
			{
				SortedList[index] = value;
			}
		}

		#endregion

		#region IBindingList

		/// <summary>
		/// Fügt den Indizes, die für die Suche verwendet werden, den <see cref="T:System.ComponentModel.PropertyDescriptor"/> hinzu.
		/// </summary>
		/// <param name="property">Der <see cref="T:System.ComponentModel.PropertyDescriptor"/>, der den Indizes hinzugefügt werden soll, die für die Suche verwendet werden.</param>
		public void AddIndex(PropertyDescriptor property)
		{
			Indices.Add(property);
		}

		/// <summary>
		/// Fügt der Liste ein neues Element hinzu.
		/// </summary>
		/// <returns>
		/// Das der Liste hinzugefügte Element.
		/// </returns>
		/// <exception cref="T:System.NotSupportedException">
		///   <see cref="P:System.ComponentModel.IBindingList.AllowNew"/> is false. </exception>
		public object AddNew()
		{
			return null;
		}

		private bool _AllowEdit = true;
		/// <summary>
		/// Ruft ab, ob Elemente der Liste aktualisiert werden können.
		/// </summary>
		/// <returns>true, wenn Elemente der Liste aktualisiert werden können, andernfalls false.</returns>
		public bool AllowEdit
		{
			get { return _AllowEdit; }
		}

		private bool _AllowNew = true;
		/// <summary>
		/// Ruft ab, ob der Liste mithilfe von <see cref="M:System.ComponentModel.IBindingList.AddNew"/> Elemente hinzugefügt werden können.
		/// </summary>
		/// <returns>true, wenn Sie der Liste mithilfe von <see cref="M:System.ComponentModel.IBindingList.AddNew"/> Elemente hinzufügen können, andernfalls false.</returns>
		public bool AllowNew
		{
			get { return _AllowNew; }
		}

		private bool _AllowRemove = true;
		/// <summary>
		/// Ruft ab, ob Elemente unter Verwendung von <see cref="M:System.Collections.IList.Remove(System.Object)"/> oder <see cref="M:System.Collections.IList.RemoveAt(System.Int32)"/> aus der Liste entfernt werden können.
		/// </summary>
		/// <returns>true, wenn Elemente aus der Liste entfernt werden können, andernfalls false.</returns>
		public bool AllowRemove
		{
			get { return _AllowRemove; }
		}

		/// <summary>
		/// Sortiert die Liste entsprechend einem <see cref="T:System.ComponentModel.PropertyDescriptor"/> und einer <see cref="T:System.ComponentModel.ListSortDirection"/>.
		/// </summary>
		/// <param name="property">Der <see cref="T:System.ComponentModel.PropertyDescriptor"/>, nach dem sortiert werden soll.</param>
		/// <param name="direction">One of the <see cref="T:System.ComponentModel.ListSortDirection"/> values.</param>
		/// <exception cref="T:System.NotSupportedException">
		///   <see cref="P:System.ComponentModel.IBindingList.SupportsSorting"/> ist false. </exception>
		public void ApplySort(PropertyDescriptor property, ListSortDirection direction)
		{
			SortDescriptions = null;
			ObjectComparer comparer = new ObjectComparer(new ListSortDescriptionCollection(new ListSortDescription[] {new ListSortDescription(property, direction)}));

			SortedList.Sort(comparer);

			IsSorted = true;
			SortProperty = property;
			SortDirection = direction;
		}

		/// <summary>
		/// Gibt den Index der Zeile mit dem angegebenen <see cref="T:System.ComponentModel.PropertyDescriptor"/> zurück.
		/// </summary>
		/// <param name="property">Der <see cref="T:System.ComponentModel.PropertyDescriptor"/>, in dem gesucht werden soll.</param>
		/// <param name="key">Der Wert des <paramref name="property"/>-Parameters, nach dem gesucht werden soll.</param>
		/// <returns>
		/// Der Index der Zeile mit dem angegebenen <see cref="T:System.ComponentModel.PropertyDescriptor"/>.
		/// </returns>
		/// <exception cref="T:System.NotSupportedException">
		///   <see cref="P:System.ComponentModel.IBindingList.SupportsSearching"/> ist false. </exception>
		public int Find(PropertyDescriptor property, object key)
		{
			throw new NotImplementedException();
		}

		private bool _IsSorted = false;
		/// <summary>
		/// Ruft ab, ob die Element in der Liste sortiert werden.
		/// </summary>
		/// <returns>true, wenn <see cref="M:System.ComponentModel.IBindingList.ApplySort(System.ComponentModel.PropertyDescriptor,System.ComponentModel.ListSortDirection)"/> aufgerufen wurde und <see cref="M:System.ComponentModel.IBindingList.RemoveSort"/> nicht aufgerufen wurde, andernfalls false.</returns>
		///   
		/// <exception cref="T:System.NotSupportedException">
		///   <see cref="P:System.ComponentModel.IBindingList.SupportsSorting"/> ist false. </exception>
		public bool IsSorted
		{
			get { return _IsSorted; }
			protected set { _IsSorted = value; }
		}

		/// <summary>
		/// Tritt ein, wenn die Liste oder ein Element der Liste geändert wird.
		/// </summary>
		public event ListChangedEventHandler ListChanged;

		/// <summary>
		/// Entfernt den <see cref="T:System.ComponentModel.PropertyDescriptor"/> aus den Indizes, die für die Suche verwendet werden.
		/// </summary>
		/// <param name="property">Der <see cref="T:System.ComponentModel.PropertyDescriptor"/>, der aus den für die Suche verwendeten Indizes entfernt werden soll.</param>
		public void RemoveIndex(PropertyDescriptor property)
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// Entfernt alle Sortierungen, die unter Verwendung von <see cref="M:System.ComponentModel.IBindingList.ApplySort(System.ComponentModel.PropertyDescriptor,System.ComponentModel.ListSortDirection)"/> angewendet wurden.
		/// </summary>
		/// <exception cref="T:System.NotSupportedException">
		///   <see cref="P:System.ComponentModel.IBindingList.SupportsSorting"/> ist false. </exception>
		public void RemoveSort()
		{
			SortProperty = null;
			SortDescriptions = null;
			SortedList.Clear();
			SortedList = new List<ItemView<T>>(FilteredList);
		}

		/// <summary>
		/// Ruft die Sortierrichtung ab.
		/// </summary>
		/// <returns>Einer der <see cref="T:System.ComponentModel.ListSortDirection"/>-Werte.</returns>
		///   
		/// <exception cref="T:System.NotSupportedException">
		///   <see cref="P:System.ComponentModel.IBindingList.SupportsSorting"/> is false. </exception>
		public ListSortDirection SortDirection
		{
			get;
			protected set;
		}

		/// <summary>
		/// Ruft den <see cref="T:System.ComponentModel.PropertyDescriptor"/> ab, der für die Sortierung verwendet wird.
		/// </summary>
		/// <returns>Der <see cref="T:System.ComponentModel.PropertyDescriptor"/>, der für die Sortierung verwendet wird.</returns>
		///   
		/// <exception cref="T:System.NotSupportedException">
		///   <see cref="P:System.ComponentModel.IBindingList.SupportsSorting"/> ist false. </exception>
		public PropertyDescriptor SortProperty
		{
			get;
			protected set;
		}

		/// <summary>
		/// Ruft ab, ob bei Änderungen der Liste oder eines Elements ein <see cref="E:System.ComponentModel.IBindingList.ListChanged"/>-Ereignis ausgelöst wird.
		/// </summary>
		/// <returns>true, wenn bei Änderungen der Liste oder eines Elements ein <see cref="E:System.ComponentModel.IBindingList.ListChanged"/>-Ereignis ausgelöst wird, andernfalls false.</returns>
		public bool SupportsChangeNotification
		{
			get { return true; }
		}

		/// <summary>
		/// Ruft ab, ob die Liste die Suche mit der <see cref="M:System.ComponentModel.IBindingList.Find(System.ComponentModel.PropertyDescriptor,System.Object)"/>-Methode unterstützt.
		/// </summary>
		/// <returns>true, wenn die Liste die Suche mit der <see cref="M:System.ComponentModel.IBindingList.Find(System.ComponentModel.PropertyDescriptor,System.Object)"/>-Methode unterstützt, andernfalls false.</returns>
		public bool SupportsSearching
		{
			get { return true; }
		}

		/// <summary>
		/// Ruft ab, ob die Liste die Sortierung unterstützt.
		/// </summary>
		/// <returns>true, wenn die Liste die Sortierung unterstützt, andernfalls false.</returns>
		public bool SupportsSorting
		{
			get { return true; }
		}

		/// <summary>
		/// Fügt der <see cref="T:System.Collections.IList"/> ein Element hinzu.
		/// </summary>
		/// <param name="value">Das Objekt, das der <see cref="T:System.Collections.IList"/> hinzugefügt werden soll.</param>
		/// <returns>
		/// Die Position, an der das neue Element eingefügt wurde, oder -1, um anzugeben, dass das Element nicht in die Auflistung eingefügt wurde.
		/// </returns>
		/// <exception cref="T:System.NotSupportedException">
		///   <see cref="T:System.Collections.IList"/> ist schreibgeschützt.– oder – <see cref="T:System.Collections.IList"/> hat eine feste Größe. </exception>
		public int Add(object value)
		{
			if (value != null && value.GetType() == typeof(ItemView<T>))
			{
				SortedList.Insert(SortedList.Count, value as ItemView<T>);
				return SortedList.Count - 1;
			}
			else
				throw new ArgumentException("The parameter 'value' is no ItemView<T>!");
		}

		/// <summary>
		/// Bestimmt, ob die <see cref="T:System.Collections.IList"/> einen bestimmten Wert enthält.
		/// </summary>
		/// <param name="value">Das in der <see cref="T:System.Collections.IList"/> zu suchende Objekt.</param>
		/// <returns>
		/// true, wenn das <see cref="T:System.Object"/> in der <see cref="T:System.Collections.IList"/> gefunden wird, andernfalls false.
		/// </returns>
		public bool Contains(object value)
		{
			if (value != null && value.GetType() == typeof(ItemView<T>))
			{
				return SortedList.Contains(value as ItemView<T>);
			}
			else
				throw new ArgumentException("The parameter 'value' is no ItemView<T>!");
		}

		/// <summary>
		/// Bestimmt den Index eines bestimmten Elements in der <see cref="T:System.Collections.IList"/>.
		/// </summary>
		/// <param name="value">Das in der <see cref="T:System.Collections.IList"/> zu suchende Objekt.</param>
		/// <returns>
		/// Der Index von <paramref name="value"/>, wenn das Element in der Liste gefunden wird, andernfalls -1.
		/// </returns>
		public int IndexOf(object value)
		{
			if (value != null && value.GetType() == typeof(ItemView<T>))
			{
				return SortedList.IndexOf(value as ItemView<T>);
			}
			else
				throw new ArgumentException("The parameter 'value' is no ItemView<T>!");
		}

		/// <summary>
		/// Fügt am angegebenen Index ein Element in die <see cref="T:System.Collections.IList"/> ein.
		/// </summary>
		/// <param name="index">Der nullbasierte Index, an dem der <paramref name="value"/> eingefügt werden soll.</param>
		/// <param name="value">Das in die <see cref="T:System.Collections.IList"/> einzufügende Objekt.</param>
		/// <exception cref="T:System.ArgumentOutOfRangeException">Der <paramref name="index"/> ist kein zulässiger Index in der <see cref="T:System.Collections.IList"/>. </exception>
		///   
		/// <exception cref="T:System.NotSupportedException">
		///   <see cref="T:System.Collections.IList"/> ist schreibgeschützt.– oder – <see cref="T:System.Collections.IList"/> hat eine feste Größe. </exception>
		///   
		/// <exception cref="T:System.NullReferenceException">
		///   <paramref name="value"/> ist ein NULL-Verweis in <see cref="T:System.Collections.IList"/>.</exception>
		public void Insert(int index, object value)
		{
			if (value != null && value.GetType() == typeof(ItemView<T>))
			{
				SortedList.Insert(index, value as ItemView<T>);
			}
			else
				throw new ArgumentException("The parameter 'value' is no ItemView<T>!");
		}

		/// <summary>
		/// Ruft einen Wert ab, der angibt, ob die <see cref="T:System.Collections.IList"/> eine feste Größe aufweist.
		/// </summary>
		/// <returns>true, wenn <see cref="T:System.Collections.IList"/> eine feste Größe hat, andernfalls false.</returns>
		public bool IsFixedSize
		{
			get { return false; }
		}

		/// <summary>
		/// Entfernt das erste Vorkommen eines bestimmten Objekts aus der <see cref="T:System.Collections.IList"/>.
		/// </summary>
		/// <param name="value">Das aus der <see cref="T:System.Collections.IList"/> zu entfernende Objekt.</param>
		/// <exception cref="T:System.NotSupportedException">
		///   <see cref="T:System.Collections.IList"/> ist schreibgeschützt.– oder – <see cref="T:System.Collections.IList"/> hat eine feste Größe. </exception>
		public void Remove(object value)
		{
			if (value != null && value.GetType() == typeof(ItemView<T>))
			{
				SortedList.Remove(value as ItemView<T>);
			}
			else
				throw new ArgumentException("The parameter 'value' is no ItemView<T>!");
		}

		/// <summary>
		/// Ruft das Element am angegebenen Index ab oder legt dieses fest.
		/// </summary>
		/// <returns>Das Element am angegebenen Index.</returns>
		///   
		/// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="index"/> ist kein gültiger Index in der <see cref="T:System.Collections.Generic.IList`1"/>.</exception>
		///   
		/// <exception cref="T:System.NotSupportedException">Die Eigenschaft wird festgelegt, und die <see cref="T:System.Collections.Generic.IList`1"/> ist schreibgeschützt.</exception>
		object System.Collections.IList.this[int index]
		{
			get
			{
				return SortedList[index];
			}
			set
			{
				if (value != null && value.GetType() == typeof(ItemView<T>))
				{
					SortedList[index] = value as ItemView<T>;
				}
				else
					throw new ArgumentException("The parameter 'value' is no ItemView<T>!");
			}
		}

		/// <summary>
		/// Kopiert die Elemente der <see cref="T:System.Collections.ICollection"/> in ein <see cref="T:System.Array"/>, beginnend bei einem bestimmten <see cref="T:System.Array"/>-Index.
		/// </summary>
		/// <param name="array">Das eindimensionale <see cref="T:System.Array"/>, das das Ziel der aus <see cref="T:System.Collections.ICollection"/> kopierten Elemente ist.Für <see cref="T:System.Array"/> muss eine nullbasierte Indizierung verwendet werden.</param>
		/// <param name="index">Der nullbasierte Index in <paramref name="array"/>, ab dem kopiert wird.</param>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="array"/> ist null. </exception>
		///   
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="index"/> ist kleiner als 0 (null). </exception>
		///   
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="array"/> ist mehrdimensional.– oder – Die Anzahl der Elemente in der Quelle <see cref="T:System.Collections.ICollection"/> ist größer als der verfügbare Speicherplatz ab <paramref name="index"/> bis zum Ende des <paramref name="array"/>, das als Ziel festgelegt wurde. </exception>
		///   
		/// <exception cref="T:System.ArgumentException">Der Typ der Quelle <see cref="T:System.Collections.ICollection"/> kann nicht automatisch in den Typ des als Ziel festgelegten <paramref name="array"/> umgewandelt werden. </exception>
		public void CopyTo(Array array, int index)
		{
			if (array == null)
				throw new ArgumentNullException("array");
			if (index < 0)
				throw new ArgumentOutOfRangeException("index");
			if ((array.Length - index) < SortedList.Count)
				throw new ArgumentException("Die Anzahl der Elemente in der Quelle ist größer als der verfügbare Speicherplatz im Array ab Index.");
		}

		public bool IsSynchronized
		{
			get { return false; }
		}

		public object SyncRoot
		{
			get { throw new NotImplementedException(); }
		}

		#endregion

		#region IBindingListView

		public void ApplySort(ListSortDescriptionCollection sorts)
		{
			SortProperty = null;
			ObjectComparer comparer = new ObjectComparer(sorts);

			SortedList.Sort(comparer);

			IsSorted = true;
			SortDescriptions = sorts;
		}

		private string _Filter = null;
		public string Filter
		{
			get
			{
				return _Filter;
			}
			set
			{
				FilterCore(value);
			}
		}

		public void RemoveFilter()
		{
			FilteredList = new List<ItemView<T>>(MappedList);
			SortCore();
		}

		public ListSortDescriptionCollection SortDescriptions { get; protected set; }

		public bool SupportsAdvancedSorting
		{
			get { return true; }
		}

		public bool SupportsFiltering
		{
			get { return true; }
		}

		#endregion

		#endregion

		#region Methods

		private void FilterCore(string filter)
		{
			if (MappedList.Count == 0)
				return;
			if (String.IsNullOrWhiteSpace(filter))
			{
				RemoveFilter();
				return;
			}


			FilteredList.Clear();
			FilterEvaluator.Context = this;
			FilterEvaluator.Parse(filter, MappedList[0].Base);

			foreach (var item in MappedList)
			{
				bool passedFilter = FilterEvaluator.Evaluate(item.Base);
				if (passedFilter)
					FilteredList.Add(item);
			}
			_Filter = filter;
			SortedList = new List<ItemView<T>>(FilteredList);
			SortCore();
		}

		private void SortCore()
		{
			if (SortProperty != null)
				ApplySort(SortProperty, SortDirection);
			else if (SortDescriptions != null)
				ApplySort(SortDescriptions);
			else
				RemoveSort();
		}

		#endregion
	}
}
